/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { AbstractFieldRef, ArkArrayRef, ArkAssignStmt, ArkAwaitExpr, ArkClass, ArkFile, ArkInstanceFieldRef, ArkInstanceInvokeExpr, ArkInvokeStmt, ArkMethod, ArkNewExpr, ArkReturnStmt, ArkStaticFieldRef, ArrayType, ClassType, ExportInfo, FunctionType, GlobalRef, Local, MethodSignature, Scene, Stmt, UNKNOWN_FILE_NAME, UNKNOWN_PROJECT_NAME, Value } from 'arkanalyzer/lib';
import { BaseChecker, BaseMetaData } from '../BaseChecker';
import { MatcherCallback } from '../../matcher/Matchers';
import { Defects } from '../../model/Defects';
import Logger, { LOG_MODULE_TYPE } from 'arkanalyzer/lib/utils/logger';
import { Rule } from '../../model/Rule';
import { IssueReport } from '../../model/Defects';
import { CheckerUtils } from '../../Index';
import { ArkExport } from 'arkanalyzer/lib/core/model/ArkExport';

const logger = Logger.getLogger(LOG_MODULE_TYPE.HOMECHECK, 'GlobalVarUpdateAsyncCheck');
const gMetaData: BaseMetaData = {
    severity: 3,
    ruleDocPath: 'docs/modules-check.md',
    description: 'Do not asynchronously modify variables in the imported class',
};
const instantiateClassSignatures: string[] = [
    '@ohosSdk/component/common_ts_ets_api.d.ts: LocalStorage',
    '@built-in/lib.es5.d.ts: String',
    '@built-in/lib.es5.d.ts: Array',
    '@built-in/lib.es5.d.ts: ArrayBuffer',
    '@built-in/lib.es5.d.ts: DataView',
    '@built-in/lib.es2020.bigint.d.ts: DataView',
    '@built-in/lib.es5.d.ts: Int8Array',
    '@built-in/lib.es5.d.ts: Uint8Array',
    '@built-in/lib.es5.d.ts: Uint8ClampedArray',
    '@built-in/lib.es5.d.ts: Int16Array',
    '@built-in/lib.es5.d.ts: Uint16Array',
    '@built-in/lib.es5.d.ts: Int32Array',
    '@built-in/lib.es5.d.ts: Uint32Array',
    '@built-in/lib.es5.d.ts: Float32Array',
    '@built-in/lib.es5.d.ts: Float64Array',
    '@built-in/lib.es2017.sharedmemory.d.ts: SharedArrayBuffer',
    '@built-in/lib.es2020.bigint.d.ts: BigInt64Array',
    '@built-in/lib.es2020.bigint.d.ts: BigUint64Array',
    '@built-in/lib.es2015.symbol.wellknown.d.ts: WeakMap<K extends object,V>',
    '@built-in/lib.es2015.symbol.wellknown.d.ts: WeakSet<T extends object>',
    '@built-in/lib.es2015.symbol.wellknown.d.ts: Map<K,V>',
    '@built-in/lib.es2015.symbol.wellknown.d.ts: Set<T>',
];
const instantiateMethodSignature: string[] = [
    '@ohosSdk/component/common_ts_ets_api.d.ts: LocalStorage.clear()',
    '@ohosSdk/component/common_ts_ets_api.d.ts: LocalStorage.delete(string)',
    '@ohosSdk/component/common_ts_ets_api.d.ts: LocalStorage.set(string, T)',
    '@ohosSdk/component/common_ts_ets_api.d.ts: LocalStorage.setAndRef(string, T)',
    '@ohosSdk/component/common_ts_ets_api.d.ts: LocalStorage.setAndProp(string, S)',
    '@ohosSdk/component/common_ts_ets_api.d.ts: LocalStorage.setAndLink(string, T)',
    '@ohosSdk/component/common_ts_ets_api.d.ts: LocalStorage.setOrCreate(string, T)',
    '@built-in/lib.es2015.symbol.wellknown.d.ts: String.replace(@built-in/lib.es2015.symbol.wellknown.d.ts: %AC2, string)',
    '@built-in/lib.es2021.string.d.ts: String.replaceAll(string|@built-in/lib.es2015.symbol.wellknown.d.ts: RegExp, string)',
    '@built-in/lib.es5.d.ts: Array.pop()',
    '@built-in/lib.es5.d.ts: Array.fill()',
    '@built-in/lib.es5.d.ts: Array.shift()',
    '@built-in/lib.es5.d.ts: Array.reverse()',
    '@built-in/lib.es5.d.ts: Array.push(T[])',
    '@built-in/lib.es5.d.ts: Array.copyWithin()',
    '@built-in/lib.es5.d.ts: Array.unshift(T[])',
    '@built-in/lib.es5.d.ts: Array.splice(number, number)',
    '@built-in/lib.es5.d.ts: Array.sort(@built-in/lib.es5.d.ts: Array.%AM0(T, T))',
    '@built-in/lib.es5.d.ts: ArrayBuffer.slice(number, number)',
    '@built-in/lib.es5.d.ts: DataView.setFloat32(number, number, boolean)',
    '@built-in/lib.es5.d.ts: DataView.setFloat64(number, number, boolean)',
    '@built-in/lib.es5.d.ts: DataView.setInt8(number, number)',
    '@built-in/lib.es5.d.ts: DataView.setInt16(number, number, boolean)',
    '@built-in/lib.es5.d.ts: DataView.setInt32(number, number, boolean)',
    '@built-in/lib.es5.d.ts: DataView.setUint8(number, number)',
    '@built-in/lib.es5.d.ts: DataView.setUint16(number, number, boolean)',
    '@built-in/lib.es5.d.ts: DataView.setUint32(number, number, boolean)',
    '@built-in/lib.es2020.bigint.d.ts: DataView.setBigInt64(number, bigint, boolean)',
    '@built-in/lib.es2020.bigint.d.ts: DataView.setBigUint64(number, bigint, boolean)',
    '@built-in/lib.es5.d.ts: Int8Array.copyWithin(number, number, number)',
    '@built-in/lib.es5.d.ts: Int8Array.fill(number, number, number)',
    '@built-in/lib.es5.d.ts: Int8Array.set(@built-in/lib.es5.d.ts: ArrayLike<number>, number)',
    '@built-in/lib.es5.d.ts: Int8Array.slice(number, number)',
    '@built-in/lib.es5.d.ts: Int8Array.subarray(number, number)',
    '@built-in/lib.es5.d.ts: Int8Array.reverse()',
    '@built-in/lib.es5.d.ts: Uint8Array.copyWithin(number, number, number)',
    '@built-in/lib.es5.d.ts: Uint8Array.fill(number, number, number)',
    '@built-in/lib.es5.d.ts: Uint8Array.set(@built-in/lib.es5.d.ts: ArrayLike<number>, number)',
    '@built-in/lib.es5.d.ts: Uint8Array.slice(number, number)',
    '@built-in/lib.es5.d.ts: Uint8Array.subarray(number, number)',
    '@built-in/lib.es5.d.ts: Uint8Array.reverse()',
    '@built-in/lib.es5.d.ts: Uint8ClampedArray.copyWithin(number, number, number)',
    '@built-in/lib.es5.d.ts: Uint8ClampedArray.fill(number, number, number)',
    '@built-in/lib.es5.d.ts: Uint8ClampedArray.set(@built-in/lib.es5.d.ts: ArrayLike<number>, number)',
    '@built-in/lib.es5.d.ts: Uint8ClampedArray.slice(number, number)',
    '@built-in/lib.es5.d.ts: Uint8ClampedArray.subarray(number, number)',
    '@built-in/lib.es5.d.ts: Uint8ClampedArray.reverse()',
    '@built-in/lib.es5.d.ts: Int16Array.copyWithin(number, number, number)',
    '@built-in/lib.es5.d.ts: Int16Array.fill(number, number, number)',
    '@built-in/lib.es5.d.ts: Int16Array.set(@built-in/lib.es5.d.ts: ArrayLike<number>, number)',
    '@built-in/lib.es5.d.ts: Int16Array.slice(number, number)',
    '@built-in/lib.es5.d.ts: Int16Array.subarray(number, number)',
    '@built-in/lib.es5.d.ts: Int16Array.reverse()',
    '@built-in/lib.es5.d.ts: Uint16Array.copyWithin(number, number, number)',
    '@built-in/lib.es5.d.ts: Uint16Array.fill(number, number, number)',
    '@built-in/lib.es5.d.ts: Uint16Array.set(@built-in/lib.es5.d.ts: ArrayLike<number>, number)',
    '@built-in/lib.es5.d.ts: Uint16Array.slice(number, number)',
    '@built-in/lib.es5.d.ts: Uint16Array.subarray(number, number)',
    '@built-in/lib.es5.d.ts: Uint16Array.reverse()',
    '@built-in/lib.es5.d.ts: Int32Array.copyWithin(number, number, number)',
    '@built-in/lib.es5.d.ts: Int32Array.fill(number, number, number)',
    '@built-in/lib.es5.d.ts: Int32Array.set(@built-in/lib.es5.d.ts: ArrayLike<number>, number)',
    '@built-in/lib.es5.d.ts: Int32Array.slice(number, number)',
    '@built-in/lib.es5.d.ts: Int32Array.subarray(number, number)',
    '@built-in/lib.es5.d.ts: Int32Array.reverse()',
    '@built-in/lib.es5.d.ts: Uint32Array.copyWithin(number, number, number)',
    '@built-in/lib.es5.d.ts: Uint32Array.fill(number, number, number)',
    '@built-in/lib.es5.d.ts: Uint32Array.set(@built-in/lib.es5.d.ts: ArrayLike<number>, number)',
    '@built-in/lib.es5.d.ts: Uint32Array.slice(number, number)',
    '@built-in/lib.es5.d.ts: Uint32Array.subarray(number, number)',
    '@built-in/lib.es5.d.ts: Uint32Array.reverse()',
    '@built-in/lib.es5.d.ts: Float32Array.copyWithin(number, number, number)',
    '@built-in/lib.es5.d.ts: Float32Array.fill(number, number, number)',
    '@built-in/lib.es5.d.ts: Float32Array.set(@built-in/lib.es5.d.ts: ArrayLike<number>, number)',
    '@built-in/lib.es5.d.ts: Float32Array.slice(number, number)',
    '@built-in/lib.es5.d.ts: Float32Array.subarray(number, number)',
    '@built-in/lib.es5.d.ts: Float32Array.reverse()',
    '@built-in/lib.es5.d.ts: Float64Array.copyWithin(number, number, number)',
    '@built-in/lib.es5.d.ts: Float64Array.fill(number, number, number)',
    '@built-in/lib.es5.d.ts: Float64Array.set(@built-in/lib.es5.d.ts: ArrayLike<number>, number)',
    '@built-in/lib.es5.d.ts: Float64Array.slice(number, number)',
    '@built-in/lib.es5.d.ts: Float64Array.subarray(number, number)',
    '@built-in/lib.es5.d.ts: Float64Array.reverse()',
    '@built-in/lib.es2017.sharedmemory.d.ts: SharedArrayBuffer.slice(number, number)',
    '@built-in/lib.es2020.bigint.d.ts: BigInt64Array.copyWithin(number, number, number)',
    '@built-in/lib.es2020.bigint.d.ts: BigInt64Array.fill(bigint, number, number)',
    '@built-in/lib.es2020.bigint.d.ts: BigInt64Array.set(@built-in/lib.es5.d.ts: ArrayLike<bigint>, number)',
    '@built-in/lib.es2020.bigint.d.ts: BigInt64Array.slice(number, number)',
    '@built-in/lib.es2020.bigint.d.ts: BigInt64Array.subarray(number, number)',
    '@built-in/lib.es2020.bigint.d.ts: BigInt64Array.reverse()',
    '@built-in/lib.es2020.bigint.d.ts: BigUint64Array.copyWithin(number, number, number)',
    '@built-in/lib.es2020.bigint.d.ts: BigUint64Array.fill(bigint, number, number)',
    '@built-in/lib.es2020.bigint.d.ts: BigUint64Array.set(@built-in/lib.es5.d.ts: ArrayLike<bigint>, number)',
    '@built-in/lib.es2020.bigint.d.ts: BigUint64Array.slice(number, number)',
    '@built-in/lib.es2020.bigint.d.ts: BigUint64Array.subarray(number, number)',
    '@built-in/lib.es2020.bigint.d.ts: BigUint64Array.reverse()',
    '@built-in/lib.es2015.collection.d.ts: WeakMap.delete(K extends object)',
    '@built-in/lib.es2015.collection.d.ts: WeakMap.set(K extends object, V)',
    '@built-in/lib.es2015.collection.d.ts: WeakSet.add(T extends object)',
    '@built-in/lib.es2015.collection.d.ts: WeakSet.delete(T extends object)',
    '@built-in/lib.es2015.collection.d.ts: Map.clear()',
    '@built-in/lib.es2015.collection.d.ts: Map.delete(K)',
    '@built-in/lib.es2015.collection.d.ts: Map.set(K, V)',
    '@built-in/lib.es2015.collection.d.ts: Set.add(T)',
    '@built-in/lib.es2015.collection.d.ts: Set.clear()',
    '@built-in/lib.es2015.collection.d.ts: Set.delete(T)',
];
const directClassSignatures: string[] = [
    '@ohosSdk/component/common_ts_ets_api.d.ts: AppStorage',
    '@ohosSdk/component/common_ts_ets_api.d.ts: Environment',
    '@ohosSdk/component/common_ts_ets_api.d.ts: PersistentStorage',
    '@built-in/lib.es2017.sharedmemory.d.ts: Atomics',
];
const directMethodSignatures: string[] = [
    '@ohosSdk/component/common_ts_ets_api.d.ts: AppStorage.[static]clear()',
    '@ohosSdk/component/common_ts_ets_api.d.ts: AppStorage.[static]delete(string)',
    '@ohosSdk/component/common_ts_ets_api.d.ts: AppStorage.[static]set(string, T)',
    '@ohosSdk/component/common_ts_ets_api.d.ts: AppStorage.[static]setAndRef(string, T)',
    '@ohosSdk/component/common_ts_ets_api.d.ts: AppStorage.[static]setAndLink(string, T)',
    '@ohosSdk/component/common_ts_ets_api.d.ts: AppStorage.[static]setAndProp(string, T)',
    '@ohosSdk/component/common_ts_ets_api.d.ts: AppStorage.[static]setOrCreate(string, T)',
    '@ohosSdk/component/common_ts_ets_api.d.ts: PersistentStorage.[static]deleteProp(string)',
    '@ohosSdk/component/common_ts_ets_api.d.ts: PersistentStorage.[static]persistProp(string, T)',
    '@ohosSdk/component/common_ts_ets_api.d.ts: PersistentStorage.[static]persistProps(@ohosSdk/component/common_ts_ets_api.d.ts: PersistPropsOptions[])',
    '@ohosSdk/component/common_ts_ets_api.d.ts: Environment.[static]envProp(string, S)',
    '@ohosSdk/component/common_ts_ets_api.d.ts: Environment.[static]envProps(@ohosSdk/component/common_ts_ets_api.d.ts: EnvPropsOptions[])',
    '@built-in/lib.es2017.sharedmemory.d.ts: Atomics.add(@built-in/lib.es5.d.ts: Int8Array|@built-in/lib.es5.d.ts: Uint8Array|@built-in/lib.es5.d.ts: Int16Array|@built-in/lib.es5.d.ts: Uint16Array|@built-in/lib.es5.d.ts: Int32Array|@built-in/lib.es5.d.ts: Uint32Array, number, number)',
    '@built-in/lib.es2017.sharedmemory.d.ts: Atomics.and(@built-in/lib.es5.d.ts: Int8Array|@built-in/lib.es5.d.ts: Uint8Array|@built-in/lib.es5.d.ts: Int16Array|@built-in/lib.es5.d.ts: Uint16Array|@built-in/lib.es5.d.ts: Int32Array|@built-in/lib.es5.d.ts: Uint32Array, number, number)',
    '@built-in/lib.es2017.sharedmemory.d.ts: Atomics.compareExchange(@built-in/lib.es5.d.ts: Int8Array|@built-in/lib.es5.d.ts: Uint8Array|@built-in/lib.es5.d.ts: Int16Array|@built-in/lib.es5.d.ts: Uint16Array|@built-in/lib.es5.d.ts: Int32Array|@built-in/lib.es5.d.ts: Uint32Array, number, number, number)',
    '@built-in/lib.es2017.sharedmemory.d.ts: Atomics.exchange(@built-in/lib.es5.d.ts: Int8Array|@built-in/lib.es5.d.ts: Uint8Array|@built-in/lib.es5.d.ts: Int16Array|@built-in/lib.es5.d.ts: Uint16Array|@built-in/lib.es5.d.ts: Int32Array|@built-in/lib.es5.d.ts: Uint32Array, number, number)',
    '@built-in/lib.es2017.sharedmemory.d.ts: Atomics.sub(@built-in/lib.es5.d.ts: Int8Array|@built-in/lib.es5.d.ts: Uint8Array|@built-in/lib.es5.d.ts: Int16Array|@built-in/lib.es5.d.ts: Uint16Array|@built-in/lib.es5.d.ts: Int32Array|@built-in/lib.es5.d.ts: Uint32Array, number, number)',
    '@built-in/lib.es2017.sharedmemory.d.ts: Atomics.or(@built-in/lib.es5.d.ts: Int8Array|@built-in/lib.es5.d.ts: Uint8Array|@built-in/lib.es5.d.ts: Int16Array|@built-in/lib.es5.d.ts: Uint16Array|@built-in/lib.es5.d.ts: Int32Array|@built-in/lib.es5.d.ts: Uint32Array, number, number)',
    '@built-in/lib.es2017.sharedmemory.d.ts: Atomics.xor(@built-in/lib.es5.d.ts: Int8Array|@built-in/lib.es5.d.ts: Uint8Array|@built-in/lib.es5.d.ts: Int16Array|@built-in/lib.es5.d.ts: Uint16Array|@built-in/lib.es5.d.ts: Int32Array|@built-in/lib.es5.d.ts: Uint32Array, number, number)',
    '@built-in/lib.es2017.sharedmemory.d.ts: Atomics.store(@built-in/lib.es5.d.ts: Int8Array|@built-in/lib.es5.d.ts: Uint8Array|@built-in/lib.es5.d.ts: Int16Array|@built-in/lib.es5.d.ts: Uint16Array|@built-in/lib.es5.d.ts: Int32Array|@built-in/lib.es5.d.ts: Uint32Array, number, number)',
];
const definePropertySignature = '@built-in/lib.es5.d.ts: ObjectConstructor.defineProperty' +
    '(T, @built-in/lib.es5.d.ts: %dflt.[static]%dflt()#PropertyKey, ' +
    '@built-in/lib.es5.d.ts: PropertyDescriptor&@built-in/lib.es5.d.ts: ThisType<any>)';
const promiseSignature = '@built-in/lib.es5.d.ts: Promise.then(@built-in/lib.es5.d.ts: Promise.%AM0(T)|undefined|null, @built-in/lib.es5.d.ts: Promise.%AM1(any)|undefined|null)';
export class GlobalVarUpdateAsyncCheck implements BaseChecker {
    readonly metaData: BaseMetaData = gMetaData;
    public rule: Rule;
    public defects: Defects[] = [];
    public issues: IssueReport[] = [];
    public g_exportSig = new Map<string, string>();
    public g_classSig = new Map<string, string>();
    public registerMatchers(): MatcherCallback[] {
        const matchBuildCb: MatcherCallback = {
            matcher: undefined,
            callback: this.check,
        };
        return [matchBuildCb];
    }

    public check = (scene: Scene): void => {
        let targetFiles = scene.getFiles();
        this.findExport(targetFiles);
        this.fileProcess(targetFiles);
    };

    private findExport(targetFiles: ArkFile[]): void {
        for (let targetFile of targetFiles) {
            let filePath = targetFile.getFilePath();
            let exportInfos = targetFile.getExportInfos();
            for (let exportInfo of exportInfos) {
                let arkExport = exportInfo.getArkExport();
                if (!arkExport) {
                    continue;
                }
                this.arkExportArrayType(arkExport, exportInfo, filePath);
                this.arkExportClassType(arkExport, filePath);
                if (arkExport instanceof ArkClass) {
                    this.arkExportClass(arkExport);
                }
                this.arkExportClassInstance(arkExport, targetFile.getScene());
            }
        }
    }

    private fileProcess(targetFiles: ArkFile[]): void {
        for (let targetFile of targetFiles) {
            let importInfos = targetFile.getImportInfos();
            if (importInfos.length === 0) {
                continue;
            }
            // 类
            let importClassSig = new Map<string, string>();
            for (let importInfo of importInfos) {
                let arkImport = importInfo.getLazyExportInfo();
                let arkExport = arkImport?.getArkExport();
                if (!arkExport || !(arkExport instanceof ArkClass)) {
                    continue;
                }
                // 导入时有new场景
                this.arkStaticFieldsProcess(arkExport);
                let classSig = arkExport.getSignature().toString();
                importClassSig.set(classSig, targetFile.getFilePath());
            }

            for (let arkClass of targetFile.getClasses()) {
                let methods = arkClass.getMethods();
                for (let method of methods) {
                    this.findSymbolInMethod(method, importClassSig);
                }
            }
        }
    }

    private arkStaticFieldsProcess(arkClass: ArkClass): void {
        let fields = arkClass.getFields();
        for (let field of fields) {
            if (!field.isStatic()) {
                continue;
            }
            let staticFieldSig = field.getSignature().toString();
            let initStmts = field.getInitializer();
            for (let stmt of initStmts) {
                if (!(stmt instanceof ArkAssignStmt)) {
                    continue;
                }
                let leftOp = stmt.getLeftOp();
                if (!(leftOp instanceof ArkStaticFieldRef)) {
                    continue;
                }
                let leftOpSig = leftOp.getFieldSignature().toString();
                if (leftOpSig !== staticFieldSig) {
                    continue;
                }
                let rightOp = stmt.getRightOp();
                let type = rightOp.getType();
                if (!(type instanceof ClassType)) {
                    continue;
                }
                let classSig = type.getClassSignature();
                let scene = arkClass.getDeclaringArkFile().getScene();
                let arkStaticClass = scene.getClass(classSig);
                if (!arkStaticClass) {
                    continue;
                }
                let allMethods = arkStaticClass.getMethods();
                let constructor = allMethods.find(method => method.getName() === 'constructor');
                if (constructor) {
                    let busyMethods = new Set<MethodSignature>();
                    this.findMethodProcess(constructor, busyMethods);
                }
            }
        }
    }

    private arkExportArrayType(arkExport: ArkExport, exportInfo: ExportInfo, filePath: string): void {
        if (!(arkExport instanceof Local)) {
            return;
        }
        let sig = arkExport.getSignature().toString();
        let type = arkExport.getType();
        if (type instanceof ArrayType) {
            this.g_exportSig.set(sig, filePath);
        }
    }

    private arkExportClassType(arkExport: ArkExport, filePath: string): void {
        if (!(arkExport instanceof Local)) {
            return;
        }
        let sig = arkExport.getSignature().toString();
        let type = arkExport.getType();
        if ((type instanceof ClassType) && instantiateClassSignatures.includes(type.toString())) {
            this.g_exportSig.set(sig, filePath);
        }
    }

    private arkExportClassInstance(arkExport: ArkExport, scene: Scene): void {
        if (!(arkExport instanceof Local)) {
            return;
        }
        let type = arkExport.getType();
        if (!(type instanceof ClassType)) {
            return;
        }
        if (instantiateClassSignatures.includes(type.toString())) {
            return;
        }
        let classSig = type.getClassSignature();
        let arkClass = scene.getClass(classSig);
        if (!arkClass) {
            return;
        }
        this.arkExportClass(arkClass);
    }

    private arkExportClass(arkClass: ArkClass): void {
        this.g_classSig.set(arkClass.getSignature().toString(), arkClass.getDeclaringArkFile().getFilePath());
        let fields = arkClass.getFields();
        if (fields.length === 0) {
            return;
        }
        let fieldSigStrs: string[] = [];
        for (let field of fields) {
            let fieldSignature = field.getSignature();
            let fieldSignatureStr = field.getSignature().toString();
            fieldSigStrs.push(fieldSignatureStr);
            this.g_exportSig.set(fieldSignatureStr, arkClass.getDeclaringArkFile().getFilePath());
        }
        let methods = arkClass.getMethods();
        for (let method of methods) {
            if (this.isHasChangeFieldInMethod(method, fieldSigStrs)) {
                this.g_exportSig.set(method.getSignature().toString(), arkClass.getDeclaringArkFile().getFilePath());
            }
        }
    }

    private isHasChangeFieldInMethod(method: ArkMethod, fieldSigStrs: string[]): boolean {
        let stmts = method.getCfg()?.getStmts();
        if (!stmts) {
            return false;
        }
        for (let stmt of stmts) {
            if (!(stmt instanceof ArkAssignStmt)) {
                continue;
            }
            let leftOp = stmt.getLeftOp();
            if (!(leftOp instanceof ArkInstanceFieldRef)) {
                continue;
            }
            if (fieldSigStrs.includes(leftOp.getFieldSignature().toString())) {
                return true;
            }
        }
        return false;
    }

    private findSymbolInMethod(arkMethod: ArkMethod, importClassSig: Map<string, string>): void {
        const cfgStmts = arkMethod.getBody()?.getCfg().getStmts();
        if (!cfgStmts) {
            return;
        }
        for (let stmt of cfgStmts) {
            // 场景1，调用class接口
            let invokeExpr = CheckerUtils.getInvokeExprFromStmt(stmt);
            if (!invokeExpr) {
                continue;
            }
            let cacheInvokeSignature = invokeExpr.getMethodSignature();
            let declaClass = invokeExpr.getMethodSignature().getDeclaringClassSignature().toString();
            if (!importClassSig.has(declaClass)) {
                continue;
            }
            let method = arkMethod.getDeclaringArkFile().getScene().getMethod(cacheInvokeSignature);
            if (!method) {
                continue;
            }
            let busyMethods = new Set<MethodSignature>();
            this.findMethodProcess(method, busyMethods);
        }
    }

    private arkNewProcess(stmt: Stmt): boolean {
        const invokeExpr = CheckerUtils.getInvokeExprFromStmt(stmt);
        if (!invokeExpr) {
            return false;
        }
        if (invokeExpr instanceof ArkNewExpr) {
            return true;
        }
        return false;
    }

    private arkMethodProcess(stmt: Stmt, arkMethod: ArkMethod): boolean {
        const invokeExpr = CheckerUtils.getInvokeExprFromStmt(stmt);
        if (!invokeExpr) {
            return false;
        }
        const cacheInvokeSignature = invokeExpr.getMethodSignature();
        let scene = arkMethod.getDeclaringArkFile().getScene();
        let method = scene.getMethod(cacheInvokeSignature);
        if (!method) {
            return false;
        }
        let busyMethods = new Set<MethodSignature>();
        let isWarning = this.findPromiseRecursionMethodProcess(method, busyMethods);
        if (isWarning) {
            return true;
        }
        return false;
    }

    public findPromiseRecursionMethodProcess(arkMethod: ArkMethod, busyMethods: Set<MethodSignature>): boolean {
        let usedStmts = this.globalsProcess(arkMethod);
        if (usedStmts && usedStmts.length > 0) {
            return true;
        }
        const cfgStmts = arkMethod.getBody()?.getCfg().getStmts();
        if (!cfgStmts) {
            return false;
        }
        const curMethodSignature = arkMethod.getSignature();
        busyMethods.add(curMethodSignature);
        for (let stmt of cfgStmts) {
            if (this.arkArrayProcess(stmt, arkMethod) || this.instantiateProcess(stmt, arkMethod) ||
                this.arkClassProcess(stmt, arkMethod) || this.directMethodProcess(stmt, arkMethod) ||
                this.prototypeProcess(stmt, arkMethod)) {
                return true;
            }
            const invokeExpr = CheckerUtils.getInvokeExprFromStmt(stmt);
            if (!invokeExpr) {
                continue;
            }
            const cacheInvokeSignature = invokeExpr.getMethodSignature();
            let invokeSignatureStr = cacheInvokeSignature.toString();
            if (busyMethods.has(cacheInvokeSignature) || invokeSignatureStr.includes(`@${UNKNOWN_PROJECT_NAME}/${UNKNOWN_FILE_NAME}`)) {
                continue;
            }
            let scene = arkMethod.getDeclaringArkFile().getScene();
            let invokeMethod = scene.getMethod(cacheInvokeSignature);
            if (invokeMethod === null) {
                continue;
            }
            if (this.findPromiseRecursionMethodProcess(invokeMethod, busyMethods)) {
                return true;
            }
        }
        busyMethods.delete(curMethodSignature);
        return false;
    }

    private findMethodProcess(arkMethod: ArkMethod, busyMethods: Set<MethodSignature>): void {
        // 场景1，找import
        const cfgStmts = arkMethod.getBody()?.getCfg().getStmts();
        if (!cfgStmts) {
            return;
        }
        const curMethodSignature = arkMethod.getSignature();
        busyMethods.add(curMethodSignature);
        for (let stmt of cfgStmts) {
            if (stmt instanceof ArkAssignStmt) {
                this.arkAssigeProcess(stmt, arkMethod);
            }
            if (stmt instanceof ArkReturnStmt) {
                this.arkReturnProcess(stmt, arkMethod, busyMethods);
            }
            const invokeExpr = CheckerUtils.getInvokeExprFromStmt(stmt);
            if (!invokeExpr) {
                continue;
            }
            const cacheInvokeSignature = invokeExpr.getMethodSignature();
            let invokeSignatureStr = cacheInvokeSignature.toString();
            if (busyMethods.has(cacheInvokeSignature) || invokeSignatureStr.includes(`@${UNKNOWN_PROJECT_NAME}/${UNKNOWN_FILE_NAME}`)) {
                continue;
            }
            if (promiseSignature === invokeSignatureStr) {
                let arg = invokeExpr.getArg(0);
                let type = arg.getType();
                if (!(type instanceof FunctionType)) {
                    continue;
                }
                let methodSignature = type.getMethodSignature();
                let promiseMethod = arkMethod.getDeclaringArkFile().getScene().getMethod(methodSignature);
                if (!promiseMethod) {
                    continue;
                }
                // 异步方法，有修改就告警
                this.findPromiseMethodProcess(promiseMethod, busyMethods);
                continue;
            }
            let scene = arkMethod.getDeclaringArkFile().getScene();
            this.findSymbolInvokeStmt(stmt, scene, busyMethods);
            let invokeMethod = scene.getMethod(cacheInvokeSignature);
            if (invokeMethod === null) {
                continue;
            }
            this.findMethodProcess(invokeMethod, busyMethods);
        }
        busyMethods.delete(curMethodSignature);
    }

    private arkAssigeProcess(stmt: ArkAssignStmt, arkMethod: ArkMethod): void {
        let leftOp = stmt.getLeftOp();
        if (leftOp instanceof AbstractFieldRef) {
            let leftOpSig = leftOp.getFieldSignature().toString();
            let rightOp = stmt.getRightOp();
            if (this.g_exportSig.has(leftOpSig) && rightOp instanceof ArkAwaitExpr) {
                this.reportIssue(arkMethod.getDeclaringArkFile(), stmt);
            }
        }
    }

    private arkReturnProcess(stmt: ArkReturnStmt, arkMethod: ArkMethod, busyMethods: Set<MethodSignature>): void {
        let op = stmt.getOp();
        if (op instanceof Local && op.getType() instanceof ClassType) {
            let declar = op.getDeclaringStmt();
            if (!(declar instanceof ArkAssignStmt)) {
                return;
            }
            let rightOp = declar.getRightOp();
            if (!(rightOp instanceof ArkStaticFieldRef)) {
                return;
            }
            let fieldSignature = rightOp.getFieldSignature();
            let type = fieldSignature.getType();
            if (!(type instanceof ClassType)) {
                return;
            }
            let scene = arkMethod.getDeclaringArkFile().getScene();
            let classSig = type.getClassSignature();
            let arkClass = scene.getClass(classSig);
            if (arkClass) {
                let allMethods = arkClass.getMethods();
                let constructor = allMethods.find(method => method.getName() === 'constructor');
                if (constructor) {
                    this.findMethodProcess(constructor, busyMethods);
                }
            }
        }
    }

    private findPromiseMethodProcess(arkMethod: ArkMethod, busyMethods: Set<MethodSignature>): void {
        let usedStmts = this.globalsProcess(arkMethod);
        if (usedStmts) {
            usedStmts.forEach((stmt) => {
                this.reportIssue(arkMethod.getDeclaringArkFile(), stmt);
            });
        }
        const cfgStmts = arkMethod.getBody()?.getCfg().getStmts();
        if (!cfgStmts) {
            return;
        }
        for (let stmt of cfgStmts) {
            if (this.arkNewProcess(stmt)) {
                continue;
            }
            if (this.arkArrayProcess(stmt, arkMethod)) {
                this.reportIssue(arkMethod.getDeclaringArkFile(), stmt);
                continue;
            }
            if (this.instantiateProcess(stmt, arkMethod)) {
                this.reportIssue(arkMethod.getDeclaringArkFile(), stmt);
                continue;
            }
            if (this.arkClassProcess(stmt, arkMethod)) {
                this.reportIssue(arkMethod.getDeclaringArkFile(), stmt);
                continue;
            }
            if (this.directMethodProcess(stmt, arkMethod)) {
                this.reportIssue(arkMethod.getDeclaringArkFile(), stmt);
                continue;
            }
            if (this.prototypeProcess(stmt, arkMethod)) {
                this.reportIssue(arkMethod.getDeclaringArkFile(), stmt);
                continue;
            }
            if (this.arkMethodProcess(stmt, arkMethod)) {
                this.reportIssue(arkMethod.getDeclaringArkFile(), stmt);
                continue;
            }
        }
    }

    private findSymbolInvokeStmt(stmt: Stmt, scene: Scene, busyMethods: Set<MethodSignature>): void {
        let invokeArgs = CheckerUtils.getInvokeExprFromStmt(stmt)?.getArgs();
        if (invokeArgs) {
            this.findSymbolInArgs(invokeArgs, scene, busyMethods);
        }
    }

    private findSymbolInArgs(invokeArgvs: Value[], scene: Scene, busyMethods: Set<MethodSignature>): void {
        for (let argv of invokeArgvs) {
            let type = argv.getType();
            if (!(type instanceof FunctionType)) {
                continue;
            }
            let methodSignature = type.getMethodSignature();
            let anonymousMethod = scene.getMethod(methodSignature);
            if (anonymousMethod !== null && !busyMethods.has(anonymousMethod.getSignature())) {
                this.findMethodProcess(anonymousMethod, busyMethods);
            } else {
                logger.debug('Find FunctionType method error!');
            }
        }
    }

    private globalsProcess(method: ArkMethod): Stmt[] | undefined {
        let globals = method.getBody()?.getUsedGlobals();
        if (!globals) {
            return undefined;
        }
        let usedStmts: Stmt[] = [];
        for (let [key, value] of globals) {
            if (!(value instanceof GlobalRef)) {
                continue;
            }
            let ref = value.getRef();
            if (!ref || !(ref instanceof Local)) {
                continue;
            }
            let refSig = ref.getSignature().toString();
            if (!this.g_exportSig.has(refSig)) {
                continue;
            }
            usedStmts = usedStmts.concat(value.getUsedStmts());
        }
        return usedStmts;
    }

    private arkArrayProcess(stmt: Stmt, arkMethod: ArkMethod): boolean {
        if (!(stmt instanceof ArkAssignStmt)) {
            return false;
        }
        let leftOp = stmt.getLeftOp();
        if (leftOp instanceof ArkArrayRef) {
            let baseSignature = leftOp.getBase().getSignature().toString();
            if (!this.g_exportSig.has(baseSignature)) {
                return false;
            }
            let filePath = arkMethod.getDeclaringArkFile().getFilePath();
            if (this.g_exportSig.get(baseSignature) !== filePath) {
                return true;
            }
        }
        return false;
    }

    private instantiateProcess(stmt: Stmt, arkMethod: ArkMethod): boolean {
        if (!(stmt instanceof ArkInvokeStmt)) {
            return false;
        }
        let invokeExpr = CheckerUtils.getInvokeExprFromStmt(stmt);
        if (!invokeExpr || !(invokeExpr instanceof ArkInstanceInvokeExpr)) {
            return false;
        }
        let baseSignature = invokeExpr.getBase().getSignature().toString();
        let invokeSignature = invokeExpr.getMethodSignature().toString();
        if (this.g_exportSig.has(baseSignature) && instantiateMethodSignature.includes(invokeSignature) &&
            this.g_exportSig.get(baseSignature) !== arkMethod.getDeclaringArkFile().getFilePath()) {
            return true;
        }
        return false;
    }

    private directMethodProcess(stmt: Stmt, arkMethod: ArkMethod): boolean {
        let invokeExpr = CheckerUtils.getInvokeExprFromStmt(stmt);
        if (!invokeExpr) {
            return false;
        }
        let invokeSignature = invokeExpr.getMethodSignature().toString();
        if (directMethodSignatures.includes(invokeSignature)) {
            return true;
        }
        return false;
    }

    private arkClassProcess(stmt: Stmt, arkMethod: ArkMethod): boolean {
        if (stmt instanceof ArkAssignStmt) {
            let leftOp = stmt.getLeftOp();
            if (leftOp instanceof ArkInstanceFieldRef) {
                let leftOpSig = leftOp.getFieldSignature().toString();
                if (this.g_exportSig.has(leftOpSig)) {
                    return true;
                }
                let baseTypeSig = leftOp.getBase().getType().toString();
                if ((this.g_classSig.has(baseTypeSig) && this.g_classSig.get(baseTypeSig) !== arkMethod.getDeclaringArkFile().getFilePath()) ||
                    directClassSignatures.includes(baseTypeSig)) {
                    return true;
                }
            }
        }
        let invokeExpr = CheckerUtils.getInvokeExprFromStmt(stmt);
        if (!invokeExpr) {
            return false;
        }
        let invokeSignature = invokeExpr.getMethodSignature().toString();
        let methodName = invokeExpr.getMethodSignature().getMethodSubSignature().getMethodName();
        if (methodName === 'constructor') {
            let conMethod = arkMethod.getDeclaringArkFile().getScene().getMethod(invokeExpr.getMethodSignature());
            if (!conMethod) {
                return false;
            }
            let busyMethods = new Set<MethodSignature>();
            this.findMethodProcess(conMethod, busyMethods);
        }
        if (this.g_exportSig.has(invokeSignature) &&
            this.g_exportSig.get(invokeSignature) !== arkMethod.getDeclaringArkFile().getFilePath()) {
            return true;
        }
        if (definePropertySignature === invokeSignature) {
            let typeStr = invokeExpr.getArg(0).getType().toString();
            let filePath = arkMethod.getDeclaringArkFile().getFilePath();
            if ((this.g_classSig.has(typeStr) && this.g_classSig.get(typeStr) !== filePath) ||
                directClassSignatures.includes(typeStr)) {
                return true;
            }
        }
        return false;
    }

    private prototypeProcess(stmt: Stmt, arkMethod: ArkMethod): boolean {
        if (!(stmt instanceof ArkAssignStmt)) {
            return false;
        }
        let rightOp = stmt.getRightOp();
        if (!(rightOp instanceof Local)) {
            return false;
        }
        let rightType = rightOp.getType();
        if (!(rightType instanceof FunctionType)) {
            return false;
        }
        let leftOp = stmt.getLeftOp();
        if (!(leftOp instanceof ArkInstanceFieldRef)) {
            return false;
        }
        let baseStmt = leftOp.getBase().getDeclaringStmt();
        if (!baseStmt || !(baseStmt instanceof ArkAssignStmt)) {
            return false;
        }
        let baseRightOp = baseStmt.getRightOp();
        if (!(baseRightOp instanceof ArkInstanceFieldRef)) {
            return false;
        }
        let fieldName = baseRightOp.getFieldSignature().getFieldName();
        if (fieldName !== 'prototype') {
            return false;
        }
        let declarSig = baseRightOp.getFieldSignature().getDeclaringSignature().toString();
        if (instantiateClassSignatures.includes(declarSig) || directClassSignatures.includes(declarSig)) {
            return true;
        }
        return false;
    }

    private reportIssue(arkFile: ArkFile, stmt: Stmt): void {
        const severity = this.rule.alert ?? this.metaData.severity;
        let filePath = arkFile.getFilePath();
        if (!stmt) {
            return;
        }
        let originalPosition = stmt.getOriginPositionInfo();
        let lineNum = originalPosition.getLineNo();
        let startColumn = 1;
        let endColumn = 1;
        let defects = new Defects(lineNum, startColumn, endColumn, this.metaData.description, severity, this.rule.ruleId,
            filePath, this.metaData.ruleDocPath, true, false, false);
        this.issues.push(new IssueReport(defects, undefined));
    }
}